from fields import *
from registry import Registry

class DocumentBase(type):
    def __new__(cls, name, bases, attrs):
        supernew = super(DocumentBase, cls).__new__
        fieldtypes = {}
        references = {}
        for attrname, attrvalue in attrs.items():
            if isinstance(attrvalue, ReferenceField):
                refd_class = attrvalue.getReferredClass()
                refd_class._foreign_keys.append((name, attrname))
                references[attrname] = refd_class
            elif isinstance(attrvalue, BaseField):
                fieldtypes[attrname] = type(attrs.pop(attrname))

        newclass = supernew(cls, name, bases, attrs)
        setattr(newclass, "_field_types", fieldtypes)
        setattr(newclass, "_name", name)
        setattr(newclass, "_key", None)
        setattr(newclass, "_foreign_keys", [])
        setattr(newclass, "_reference_types", references)

        Registry()['identity_map'][name] = dict()

        return newclass

class Document(object):
    __metaclass__ = DocumentBase

    def __init__(self, **kwargs):
        self._new = True
        self._dirty = True
        self._fields = {}
        self._foreign_fields = {}
        self._key = None
        self._references = {}
        for fieldname, fieldtype in self._field_types.items():
            if issubclass(fieldtype, BaseField):
                value = kwargs[fieldname] if kwargs.has_key(fieldname) else None
                self._fields[fieldname] = fieldtype(value)
        for docname, attrname in self._foreign_keys:
            name = docname + "_" + attrname
            value = kwargs[name] if kwargs.has_key(name) else None
            self._foreign_fields[(docname, attrname)] = ForeignKeyField(value)
        for refname, reftype in self._reference_types.items():
            self._references[refname] = ReferenceField(reftype, self, refname)
        
    def __getattribute__(self, attr):
        if attr in object.__getattribute__(self, '_field_types').keys():
            return self._fields[attr].get_value()
        elif attr in object.__getattribute__(self, '_reference_types').keys():
            return self._references[attr]
        else:
            return object.__getattribute__(self, attr)

    def __setattr__(self, attr, value):
        if attr not in self._field_types.keys():
            return object.__setattr__(self, attr, value)
        self._dirty = True
        return self._fields[attr].set_value(value)

    def setForeignKey(self, docname, attrname, referenced_key):
        self._foreign_fields[(docname, attrname)].set_value(referenced_key)
        self.setDirty()        
    
    @classmethod
    def getByKey(cls, key):
        idmap = Registry()['identity_map'][cls._name]
        if idmap.has_key(key):
            return idmap[key]

        backend = Registry()['backend']
        result = backend.getByKey(cls, key)
        idmap[key] = result
        return result

    @classmethod
    def get(cls, criterias=[]):
        idmap = Registry()['identity_map'][cls._name]
        backend = Registry()['backend']
        result = backend.get(cls, criterias)
        return_list = []
        for obj in result:
            if idmap.has_key(obj._key):
                return_list.append(idmap[obj._key])
            else:
                idmap[obj._key] = obj
                return_list.append(obj)
        return return_list

    @classmethod
    def getName(cls):
        return cls._name
    
    @classmethod
    def getFieldTypes(cls):
        return cls._field_types
    
    @classmethod
    def getReferenceTypes(cls):
        return cls._reference_types
    
    def save(self):
        backend = Registry()['backend']
        if self._new:
            self._key = backend.insert(self)
            idmap = Registry()['identity_map'][self._name]
            idmap[self._key] = self
        elif self._dirty:
            backend.update(self)
        self._new = self._dirty = False

    def delete(self):
        backend = Registry()['backend']
        backend.delete(self)
        self._new = self._dirty = True
        self._key = None

    def getFields(self):
        return self._fields

    def getForeignFields(self):
        return self._foreign_fields

    def isNew(self):
        return self._new

    def isDirty(self):
        return self._dirty

    def setNew(self, value=True):
        self._new = value

    def setDirty(self, value=True):
        self._dirty = value

    def getKey(self):
        return self._key

    def setKey(self, key):
        self._key = key

    @classmethod
    def getKeyName(cls):
        return backend.getKeyName(cls)
